package deep8jvmdvm;

import utils.PrintlnUtils;

import java.util.concurrent.locks.ReentrantReadWriteLock;

/*
读写锁（ReentrantReadWriteLock）

在常见的开发中，我们经常会定义一个线程间共享的用作缓存的数据结构，比如一个较大的 Map。缓存中保存了全部的城市 Id 和城市 name 对应关系。这个大 Map 绝大部分时间提供读服务（根据城市 Id 查询城市名称等）。而写操作占有的时间很少，通常是在服务启动时初始化，然后可以每隔一定时间再刷新缓存的数据。但是写操作开始到结束之间，不能再有其他读操作进来，并且写操作完成之后的更新数据需要对后续的读操作可见。



在没有读写锁支持的时候，如果想要完成上述工作就需要使用 Java 的等待通知机制，就是当写操作开始时，所有晚于写操作的读操作均会进入等待状态，只有写操作完成并进行通知之后，所有等待的读操作才能继续执行。这样做的目的是使读操作能读取到正确的数据，不会出现脏读。



但是如果使用 concurrent 包中的读写锁（ReentrantReadWriteLock）实现上述功能，就只需要在读操作时获取读锁，写操作时获取写锁即可。当写锁被获取到时，后续的读写锁都会被阻塞，写锁释放之后，所有操作继续执行，这种编程方式相对于使用等待通知机制的实现方式而言，变得简单明了。



接下来，我们看下读写锁（ReentrantReadWriteLock）如何使用：

 */

public class TestReentrantReadWriteLock2 {
    private static final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true);
    private static String number = "0";

    public static void main(String[] args) {
        Thread thread1 = new Thread(new Reader(), "读线程 1 ");
        Thread thread2 = new Thread(new Reader(), "读线程 2 ");
        Thread thread3 = new Thread(new Writer(), "写线程");
        thread1.start();
        thread2.start();
        thread3.start();


    }

    static class Reader implements Runnable {
        @Override
        public void run() {
            for (int i = 0; i <= 10; i++) {
                lock.readLock().lock();
                PrintlnUtils.println(Thread.currentThread().getName() + " ---> Number is " + number);
                lock.readLock().unlock();
            }
        }
    }

    static class Writer implements Runnable {

        @Override
        public void run() {
            for (int i = 1; i <= 7; i += 2) {
                try {
                    lock.writeLock().lock();
                    PrintlnUtils.println(Thread.currentThread().getName() + " ---> 正在写入 " + i);
                    number = number.concat("" + i);
                } finally {
                    lock.writeLock().unlock();
                }

            }
        }
    }

    /*
         读线程 1  ---> Number is 0

        读线程 2  ---> Number is 0

        写线程 ---> 正在写入 1

        读线程 1  ---> Number is 01

        读线程 2  ---> Number is 01

        写线程 ---> 正在写入 3

        读线程 1  ---> Number is 013

        读线程 2  ---> Number is 013

        写线程 ---> 正在写入 5

        读线程 1  ---> Number is 0135

        读线程 2  ---> Number is 0135

        写线程 ---> 正在写入 7

        读线程 1  ---> Number is 01357

        读线程 2  ---> Number is 01357

        读线程 1  ---> Number is 01357

        读线程 2  ---> Number is 01357

        读线程 1  ---> Number is 01357

        读线程 2  ---> Number is 01357

        读线程 1  ---> Number is 01357

        读线程 2  ---> Number is 01357

        读线程 1  ---> Number is 01357

        读线程 2  ---> Number is 01357

        读线程 1  ---> Number is 01357

        读线程 2  ---> Number is 01357

        读线程 1  ---> Number is 01357

        读线程 2  ---> Number is 01357


        仔细查看日志，可以看出当写入操作在执行时，读取数据的操作会被阻塞。当写入操作执行成功后，读取数据的操作继续执行，并且读取的数据也是最新写入后的实时数据。


     */
}

